/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package im.yangqiang.android.ui.widget;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.Transformation;

import java.util.ArrayList;

import im.yangqiang.android.ui.R;


/**
 * 刷新的齿轮绘制
 *
 * @hide
 */
class RefreshGearDrawable extends Drawable
{
    private static final Interpolator         LINEAR_INTERPOLATOR = new LinearInterpolator();
    /**
     * The list of animators operating on this drawable.
     */
    private final        ArrayList<Animation> mAnimators          = new ArrayList<Animation>();

    /**
     * 齿轮对象
     */
    private final Gear[] mGears;
    private final Callback mCallback = new Callback()
    {
        @Override
        public void invalidateDrawable(Drawable d)
        {
            invalidateSelf();
        }

        @Override
        public void scheduleDrawable(Drawable d, Runnable what, long when)
        {
            scheduleSelf(what, when);
        }

        @Override
        public void unscheduleDrawable(Drawable d, Runnable what)
        {
            unscheduleSelf(what);
        }
    };
    private Resources mResources;
    private View      mParent;
    private Animation mAnimation;
    /**
     * 最大高度
     */
    private float mMaxHeight = -1;

    public RefreshGearDrawable(Context context, View parent)
    {
        mParent = parent;
        mResources = context.getResources();
        mGears = new Gear[5];
        for (int i = 0; i < mGears.length; ++i)
        {
            mGears[i] = new Gear(mCallback);
        }
        setupAnimators();
    }

    public void setMaxHeight(float height)
    {
        mMaxHeight = height;
        setupGear();
    }

    /**
     * 设置齿轮
     */
    private void setupGear()
    {
        float initSpeed = 5.5f;
        mGears[0].setBitmap(R.mipmap.gear1);
        final int initRadius = mGears[0].getBitmap().getWidth();
        mGears[0].setRadius(initRadius);
        mGears[0].setSpeed(initSpeed);
        mGears[1].setBitmap(R.mipmap.gear2);
        mGears[1].setRadius(initRadius * 2);
        mGears[1].setSpeed(-3);
        mGears[2].setBitmap(R.mipmap.gear2);
        mGears[2].setRadius(initRadius * 2);
        mGears[2].setSpeed(-3);
        mGears[3].setBitmap(R.mipmap.gear3);
        mGears[3].setRadius(initRadius * 3);
        mGears[3].setSpeed(3);
        mGears[4].setBitmap(R.mipmap.gear3);
        mGears[4].setRadius(initRadius * 3);
        mGears[4].setSpeed(3);

        DisplayMetrics metrics = mResources.getDisplayMetrics();
        Point initPoint = new Point(metrics.widthPixels / 2 + 20, (int) mMaxHeight / 2);
        mGears[0].setCenter(initPoint);
        int x1 = (int) (initPoint.x - mGears[0].getRadius() * 2 - 2);
        int y1 = (int) (initPoint.y + mGears[0].getRadius() * 2 + 2);
        mGears[1].setCenter(new Point(x1, y1));
        int x2 = (int) (initPoint.x + mGears[0].getRadius() * 2 + 2);
        int y2 = (int) (initPoint.y - mGears[0].getRadius() * 2 - 1);
        mGears[2].setCenter(new Point(x2, y2));
        int x3 = (int) (initPoint.x - mGears[1].getRadius() * 2 + 1);
        int y3 = (int) (initPoint.y - mGears[1].getRadius() * 1.2);
        mGears[3].setCenter(new Point(x3, y3));
        int x4 = (int) (mGears[2].getCenter().x + mGears[4].getRadius() / 2 + mGears[2].getRadius());
        int y4 = (int) (initPoint.y + mGears[0].getRadius() * 1.4);
        mGears[4].setCenter(new Point(x4, y4));
    }

    /**
     * 返回周长
     *
     * @param radius
     *
     * @return
     */
    private double getC(float radius)
    {
        return 2 * Math.PI * radius;
    }

    /**
     * 下拉回调
     *
     * @param rotation 下拉后的旋转角度
     */
    public void onPullCallback(float rotation)
    {
        setGearRotation(rotation);
}

    /**
     * 设置齿轮的旋转角度
     *
     * @param rotation
     */
    private void setGearRotation(float rotation)
    {
        for (Gear gear : mGears)
        {
            gear.setRotation(rotation * 360 * gear.getSpeed() * .2f);
        }
    }

    /**
     * 反转所有齿轮
     *
     * @param interpolatedTime
     */
    private void reverseGear(float interpolatedTime)
    {
        if (interpolatedTime <= .8f)
        {
            for (Gear gear : mGears)
            {
                gear.setRotation(gear.getRotation() - gear.getSpeed() * (.6f - interpolatedTime));
            }
        }
        else if (interpolatedTime <= .99f)
        {
            mGears[0].setRadius(mGears[0].getRadius() - (1 - interpolatedTime) * mGears[0].getRadius());
        }
    }


    @Override
    public void draw(Canvas c)
    {
        final int saveCount = c.save();
        for (Gear gear : mGears)
        {
            float left = gear.getCenter().x - gear.getRadius();
            float top = gear.getCenter().y - gear.getRadius();
            float right = gear.getCenter().x + gear.getRadius();
            float bottom = gear.getCenter().y + gear.getRadius();
            gear.draw(c, new RectF(left, top, right, bottom));
        }
        c.restoreToCount(saveCount);
    }

    @Override
    public void setAlpha(int alpha)
    {

    }

    @Override
    public void setColorFilter(ColorFilter colorFilter)
    {

    }

    @Override
    public int getOpacity()
    {
        return PixelFormat.TRANSLUCENT;
    }

    public boolean isRunning()
    {
        final ArrayList<Animation> animators = mAnimators;
        final int N = animators.size();
        for (int i = 0; i < N; i++)
        {
            final Animation animator = animators.get(i);
            if (animator.hasStarted() && !animator.hasEnded())
            {
                return true;
            }
        }
        return false;
    }

    public void start()
    {
        stop();
        mAnimation.reset();
        mParent.startAnimation(mAnimation);
    }

    public void stop()
    {
        mParent.clearAnimation();
        mGears[0].setRadius(mGears[0].getBitmap().getWidth());
    }

    public void end(Animation.AnimationListener listener)
    {
        mAnimation.setDuration(1500);
        mAnimation.setRepeatCount(0);
        mAnimation.setAnimationListener(listener);
    }

    private void setupAnimators()
    {
        final Animation animation = new Animation()
        {
            @Override
            public void applyTransformation(float interpolatedTime, Transformation t)
            {
                reverseGear(interpolatedTime);
            }
        };
        animation.setRepeatCount(Animation.INFINITE);
        animation.setRepeatMode(Animation.RESTART);
        animation.setInterpolator(LINEAR_INTERPOLATOR);
        mAnimation = animation;
    }

    /**
     * 齿轮类
     */
    private class Gear
    {
        private final RectF mTempBounds = new RectF();
        private final Paint mPaint      = new Paint();
        private final Callback mCallback;
        private       Bitmap   mBitmap;
        private       float    mCurrentRotation;
        /**
         * 半径
         */
        private       float    mRadius;
        /**
         * 速度
         */
        private       float    mSpeed;
        /**
         * 中心点
         */
        private       Point    mCenter;

        public Gear(Callback callback)
        {
            mCallback = callback;
            mPaint.setAntiAlias(true);
            mPaint.setStyle(Style.FILL);
        }

        public Bitmap getBitmap()
        {
            return mBitmap;
        }

        public void setBitmap(int resId)
        {
            mBitmap = BitmapFactory.decodeResource(mResources, resId);
        }

        public Point getCenter()
        {
            return mCenter;
        }

        public void setCenter(Point center)
        {
            mCenter = center;
        }

        public float getRadius()
        {
            return mRadius;
        }

        public void setRadius(float radius)
        {
            mRadius = radius;
            invalidateSelf();
        }

        public float getSpeed()
        {
            return mSpeed;
        }

        public void setSpeed(float speed)
        {
            mSpeed = speed;
        }

        public void draw(Canvas c, RectF bounds)
        {
            final RectF arcBounds = mTempBounds;
            arcBounds.set(bounds);
            mPaint.setColor(Color.WHITE);
            Matrix matrix = new Matrix();
            // x的缩放率
            float sx = arcBounds.width() / mBitmap.getWidth();
            // y的缩放率
            float sy = arcBounds.height() / mBitmap.getHeight();
            matrix.postScale(sx, sy);

            float dx = mBitmap.getWidth() * sx / 2;
            float dy = mBitmap.getHeight() * sy / 2;
            matrix.postTranslate(-dx, -dy);
            matrix.postRotate(mCurrentRotation);
            matrix.postTranslate(arcBounds.left + dx, arcBounds.top + dy);
            c.drawBitmap(mBitmap, matrix, mPaint);
        }

        public float getRotation()
        {
            return mCurrentRotation;
        }

        public void setRotation(float rotation)
        {
            mCurrentRotation = rotation;
            invalidateSelf();
        }

        private void invalidateSelf()
        {
            mCallback.invalidateDrawable(null);
        }
    }
}
